﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.Filters;
using Kaesoft.Hospedaje.Business.Recursos;

namespace Kaesoft.Hospedaje.Business
{
    public static class Log
    {
        #region Enums
        /// <summary>
        /// Prioridad del mensaje de log
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue")]
        public enum Priority
        {
            /// <summary>Low priority - tracing and debugging</summary>
            Low = 1,
            /// <summary>Medium priority - informational</summary>
            Medium = 2,
            /// <summary>High priority - important but non-critical</summary>
            High = 3,
            /// <summary>
            /// Critical priority - should always be logged.
            /// Errors and security warnings go here.
            /// </summary>
            Critical = 5
        }

        /// <summary>
        /// Categoria de mensaje de log
        /// </summary>
        public enum Category
        {
            ClientError = 0,
            Error = 1,
            Security = 2,
            General = 3,
            Trace = 4
        }

        /// <summary>
        /// Eventos de log
        /// </summary>
        private enum EventId
        {
            ClientError = 0,
            Error = 1,
            Security = 2,
            General = 3,
            Trace = 4
        }

        #endregion Enums

        #region Varaibles

        private const string LogExceptionPolicyName = "Cero Trámites Exception";

        #endregion

        #region Public methods
        public static void TraceStamp()
        {
            try
            {
                if (Logger.GetFilter<PriorityFilter>().ShouldLog((int)Priority.Low))
                {
                    string method = GetExecutingMethodName();
                    string message = string.Format(RecursosTexto.Culture, RecursosTexto.MensajeLog, method, DetailedDateTimeString());
                    WriteTraceEntry(message, Priority.Low);
                }
            }
            catch (Exception ex)
            {
                if (ExceptionPolicy.HandleException(ex, LogExceptionPolicyName)) { throw; }
            }
        }

        /// <summary>
        /// Escribe una entrada al log con prioridad default
        /// </summary>
        /// <param name="message">Mensaje</param>
        public static void WriteTraceEntry(string message)
        {
            WriteTraceEntry(message, Priority.Low);
        }

        /// <summary>
        /// Escribe una entrada al log con prioridad personalizada
        /// </summary>
        /// <param name="message">Mensaje</param>
        /// <param name="priority">Prioridad</param>
        public static void WriteTraceEntry(string message, Priority priority)
        {
            WriteLog(message, priority, Category.Trace, TraceEventType.Verbose, EventId.Trace);
        }

        /// <summary>
        /// Escribe una entrada al log de tipo cliente
        /// </summary>
        /// <param name="message">Mensaje</param>
        public static void WriteError(string message)
        {
            WriteLog(message, Priority.Critical, Category.ClientError, TraceEventType.Error, EventId.ClientError);
        }

        public static void WriteError(string message, Category category)
        {
            WriteLog(message, Priority.Critical, category, TraceEventType.Error, EventId.ClientError);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Escribe el mensaje al log
        /// </summary>
        /// <param name="message">Mensaje</param>
        /// <param name="priority">Prioridad</param>
        /// <param name="category">Categoría</param>
        /// <param name="traceEventType">Tipo</param>
        /// <param name="eventId">Evento</param>
        private static void WriteLog(string message, Priority priority, Category category, TraceEventType traceEventType, EventId eventId)
        {
            // Get category name
            string cat = Enum.GetName(typeof(Category), category);

            // Check if logging is enabled first. Saves us from
            // collating log entry info if logging is disabled.
            if (Logger.GetFilter<LogEnabledFilter>().Enabled)
            {
                LogEntry logEntry = new LogEntry();

                logEntry.EventId = (int)eventId;
                logEntry.Categories.Clear();
                logEntry.Categories.Add(cat);
                logEntry.Priority = (int)priority;
                logEntry.Severity = traceEventType;
                logEntry.Message = message;

                // Check if the categories and priority on this entry
                // will pass filtering to be logged.
                if (Logger.ShouldLog(logEntry))
                {
                    // Now gather extended info for logging
                    GetExtendedLogInfo(logEntry.ExtendedProperties);

                    // Log entry, including extended information
                    Logger.Write(logEntry);
                }
            }
        }

        /// <summary>
        /// Informacion extra para escribir al log
        /// </summary>
        /// <param name="dictionary">Lista de objetos</param>
        private static void GetExtendedLogInfo(IDictionary<string, object> dictionary)
        {
            dictionary.Add("Method", GetExecutingMethodName());
            dictionary.Add("DateTime", DetailedDateTimeString());
        }

        /// <summary>
        /// Obtiene el nombre del metodo que se esta ejecutando
        /// </summary>
        /// <returns>Metodo y tipo</returns>
        private static string GetExecutingMethodName()
        {
            string result = "Unknown";

            StackTrace trace = new StackTrace(false);

            // Find the method in the stack before a call was
            // made to the Log helper object
            for (int index = 0; index < trace.FrameCount; ++index)
            {
                StackFrame frame = trace.GetFrame(index);
                MethodBase method = frame.GetMethod();
                if (method.DeclaringType != typeof(Log))
                {
                    if (method.DeclaringType != null)
                        result = string.Concat(method.DeclaringType.FullName, ".", method.Name);
                    break;
                }
            }

            return result;
        }

        /// <summary>
        /// Formatea la fecha
        /// </summary>
        /// <returns>Fecha</returns>
        private static string DetailedDateTimeString()
        {
            return DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff",
                System.Globalization.CultureInfo.CurrentCulture);
        }

        #endregion
    }
}